﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using UICommon;
using Adastra.RichEditorLibrary;

namespace HumanitiesBubbles
{
    public class MetadataItem : Grid
    {
        Button           _del      = new Button();
        TextBlock        _label    = new TextBlock();
        Button           _is       = new Button();
        FrameworkElement _value    = null;
        Button           _subfield = new Button();

        static MenuItem showValue(TextBox value, ContextMenu cm, List<string> values, string line)
        {
            if (values.Contains(line) || line == "")
                return null;
            values.Add(line);
            MenuItem item = new MenuItem();
            item.Tag = value;
            item.Header = line;
            item.Click += new RoutedEventHandler((s, e) => value.Text += (value.Text == "" ? "" : "\r\n") + (string)item.Header);
            return item;
        }
        static IEnumerable<string> getLines(string value)
        {
            while (value.IndexOf("\r\n") != -1)
            {
                yield return value.Substring(0, value.IndexOf("\r\n"));
                value = value.Substring(value.IndexOf("\r\n") + 2);
            }
            yield return value;
        }

        void showValues(TextBox value, string field)
        {
            ContextMenu                  cm     = new ContextMenu();
            List<string>                 values = new List<string>();
            SortedList<string, MenuItem> items  = new SortedList<string, MenuItem>();
            if (field == "Creator")
            {
                foreach (DoqData d in DoqDataManager.Doqs)
                    if (d.Metadata.ContainsField(DoqMetadata.Person))
                    {
                        var val = showValue(value, cm, values, d.Metadata.get(DoqMetadata.Person));
                        if (val != null && !items.ContainsKey(d.Metadata.get(DoqMetadata.Person)))
                            items.Add(d.Metadata.get(DoqMetadata.Person), val);
                    }
            }
            else if (field == "Keywords")
            {
                foreach (var key in DoqDataKeywordExt.AllKeywords)
                    items.Add(key, showValue(value, cm, values, key));
            }
            else if (field == "Type")
            {
                foreach (DoqData d in DoqDataManager.Doqs)
                    if (d.Type != DoqData.DoqType.Empty)
                    {
                        var val = showValue(value, cm, values, d.Type.ToString());
                        if (val != null && !items.ContainsKey(d.Type.ToString()))
                            items.Add(d.Type.ToString(), val);
                    }
            }
            else
                foreach (DoqData d in DoqDataManager.Doqs)
                    foreach (KeyValuePair<string, object> pair in d.Metadata)
                        if (pair.Key == field && pair.Value is string)
                            foreach (string line in getLines((string)pair.Value))
                            {
                                var val = showValue(value, cm, values, line);
                                if (val != null && !items.ContainsKey(line))
                                    items.Add(line, val);
                            }
            foreach (var pair in items)
                cm.Items.Add(pair.Value);

            cm.IsOpen = true;
        }
        void addSubField_Click(object sender, RoutedEventArgs e)
        {
            if (_value is TextBox && (_value as TextBox).Text == "")
                return;
            _subfield.Visibility = Visibility.Collapsed;
            if (SubfieldAddedEvent != null)
                SubfieldAddedEvent(this, null);
        }
        void initDeleteButton(string field, bool enabled, int column)
        {
            _del.Margin = new Thickness(0);
            _del.BorderThickness = new Thickness(0);
            _del.Padding = new Thickness(0);
            _del.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center;
            _del.VerticalContentAlignment = System.Windows.VerticalAlignment.Center;
            _del.Content = new Icon_Cancel();
            (_del.Content as Icon_Cancel).Width = (_del.Content as Icon_Cancel).Height = 14;
            _del.Visibility = Visibility.Collapsed;
            Grid.SetColumn(_del, column);
            var c = new ColumnDefinition();
            c.Name  = "Delete";
            c.Width = new GridLength(15);
            ColumnDefinitions.Add(c);
            Children.Add(_del);
            _del.Click += new RoutedEventHandler((ss, ee) =>
            {
                if (DeletedEvent != null)
                    DeletedEvent(this, null);
            });
        }
        void initFieldName(string field, int column)
        {
            _label.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            _label.Text = field == "Creator" ? "__Creator" : field;
            _label.FontSize = 12;
            _label.Margin = new Thickness(2, 0, 2, 0);

            Grid.SetColumn(_label, column);
            var c = new ColumnDefinition();
            c.Name = "Field";
            ColumnDefinitions.Add(c);
            Children.Add(_label);
        }
        void initIsNotField(bool not, int column)
        {
            _is.Content = not ? " ≠ " : " = ";
            _is.Click += new RoutedEventHandler((sender, e) => {
                Not = !Not;
                _is.Content = Not ? " ≠ " : " = ";
            });
            Grid.SetColumn(_is, column);
            var c = new ColumnDefinition();
            c.Name = "Not";
            ColumnDefinitions.Add(c);
            c.Width = new GridLength(20);
            Children.Add(_is);
        }
        void initFieldValueDropdown(FrameworkElement value, int column)
        {
            Button drop = new Button();
            drop.Padding = new Thickness(0);
            drop.Margin = new Thickness(0);
            drop.BorderThickness = new Thickness(0);
            drop.Content = HumanitiesBubbles.Properties.Resources.dropdown.LoadImage();
            Grid.SetColumn(drop, column);
            var c = new ColumnDefinition();
            c.Name = "DropDown";
            c.Width = new GridLength(17);
            ColumnDefinitions.Add(c);
            Children.Add(drop);
            if (value is TextBox && (value as TextBox).IsEnabled)
                drop.Click += new RoutedEventHandler((sender, e) => showValues(value as TextBox, Field));
            else drop.Visibility = Visibility.Collapsed;
        }
        void initFieldValue(string field, FrameworkElement value, int column)
        {
            _value = value;
            Value = value;
            Grid.SetColumn(value, column);
            value.Tag = field;
            var c = new ColumnDefinition();
            c.Name = "Value";
            ColumnDefinitions.Add(c);
        }
        void initSubfieldButton(FrameworkElement value, int column)
        {
            _subfield = new Button();
            _subfield.Content = "+";
            _subfield.Click += new RoutedEventHandler(addSubField_Click);
            Children.Add(_subfield);
            Grid.SetColumn(_subfield, column);
            var c4 = new ColumnDefinition();
            c4.Width = new GridLength(10);
            c4.Name = "Subfield";
            ColumnDefinitions.Add(c4);
            _subfield.Visibility = Visibility.Collapsed;
            if (value is TextBox && (value as TextBox).IsEnabled)
                _subfield.Visibility = Visibility.Visible;
        }

        public event EventHandler SubfieldAddedEvent;
        public event EventHandler DeletedEvent;
        public MetadataItem(string field, bool not, FrameworkElement value)
        {
            int colId = 0;

            Background = new SolidColorBrush(Color.FromArgb(0x1, 0x1, 0x1, 0x1));

            Field = field;

            // First column : Delete button
            initDeleteButton(field, value is TextBox && (value as TextBox).IsEnabled, colId++);

            // second column: field name
            initFieldName(field, colId++);

            // third column: is/not flag
            initIsNotField(not, colId++);

            // fourth column: field value
            initFieldValue(field, value, colId++);

            //// fifth column: subfield Button
            //initSubfieldButton(value, colId++);

            // sixth column: dropdown button
            initFieldValueDropdown(value, colId++);
            MouseEnter += new MouseEventHandler((s, e) => _del.Visibility = value is TextBox && (Field == DoqMetadata.Folder || Field==DoqMetadata.Filter || (value as TextBox).IsEnabled) ? Visibility.Visible : Visibility.Collapsed);
            MouseLeave += new MouseEventHandler((s, e) => _del.Visibility = Visibility.Collapsed);
        }
        public double           DesiredFieldWidth { get  { return TextHelpers.EstimateTextWidth(Field, _label.FontFamily.GetTypefaces().First()) + 10; } }
        public string           Field             { get; set; }
        public bool             Not               { get; set; }
        public double           FieldWidth        
        {
            get { return ColumnDefinitions[1].Width.Value; }
            set { ColumnDefinitions[1].Width = new GridLength(value); }
        }
        public FrameworkElement Value             
        {
            get { return _value; }
            set
            {
                Children.Remove(Value);
                Grid.SetColumn(value, 3);
                Children.Add(value);
            }
        }
        public bool             FilterUI          
        {
            set
            {
                _is.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
                _subfield.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
                ColumnDefinitions[2].Width = new GridLength(value ? 20 : 0);
            }
        }


    }
    /// <summary>
    /// Interaction logic for MetadataViewer.xaml
    /// </summary>
    public partial class MetadataViewer : UserControl
    {
        public class FieldDesc
        {
            public string  Name  { get; set; }
            public object  Value { get; set; }
            public bool    Not   { get; set; }
            public FieldDesc(string name, object value, bool not) { Name = name; Value = value; Not = not; } 
        }

        Dictionary<string, string> _queuedUpdates = new Dictionary<string, string>();
        Guid               _creatorID = Guid.Empty;
        bool               _showFilteringUI = false;
        bool               _showCreator = true;
        TextBlock          _creator  = null; // special fields to display when filtering
        TextBlock          _URL      = null;
        TextBlock          _type     = null;
        TextBlock          _keywords = null;
        TextBlock          _deleted  = null;
        MetadataDict       _values   = null;

        FrameworkElement makeRowValue(string field, object content, bool enabled)
        {
            if (content is MetadataDictionary)
            {
                MetadataDict md = new MetadataDict();
                md.AddDict(content as MetadataDictionary, _creatorID);
                MetadataViewer value = new MetadataViewer(md, _creatorID, false, "");
                value.MinWidth = 50;
                (value as MetadataViewer).DataEditedEvent   += new DataEdited  ((viewer, fld, val) => MetadataViewer_DataEditedEvent(this, field, value._values));
                (value as MetadataViewer).FieldAddedEvent   += new FieldChanged((viewer, fld)      => MetadataViewer_DataEditedEvent(this, field, value._values));
                (value as MetadataViewer).FieldRemovedEvent += new FieldChanged((viewer, fld)      => MetadataViewer_DataEditedEvent(this, field, value._values));
                return value;
            }
            else
            {
                string contentStr = content as string;
                TextBox value = new TextBox();
                value.Background = Brushes.Wheat;
                value.BorderThickness = new Thickness(0, 0, 0, 1);
                value.Text = contentStr;
                value.AcceptsReturn = true;
                value.MinWidth = 30;
                value.TextChanged += new TextChangedEventHandler(tb_TextChanged);
                if (!PreferencesManager.EnableSystemMetadataEditing)
                    if ((!ShowAllFields && field[0] == '_') || !enabled)
                        value.IsEnabled = false;
                return value;
            }
        }
        void      item_SubfieldAddedEvent(object sender, EventArgs e)
        {
            MetadataItem item = sender as MetadataItem;
            MetadataDictionary newRow = new MetadataDictionary();
            _values.set(item.Field, newRow);
            newRow.add((item.Value as TextBox).Text, "");
            if (DataEditedEvent != null)
                DataEditedEvent(this, item.Field, newRow);
            FrameworkElement newValue = makeRowValue(item.Field, newRow, true);
            item.Value = newValue;
        }
        void      item_DeletedEvent(object sender, EventArgs e)
        {
            MetadataItem item = sender as MetadataItem;
            _values.RemoveField(item.Field);
            if (item.Tag is TextBlock)
                (item.Tag as TextBlock).Visibility = Visibility.Collapsed;
            item.Visibility = Visibility.Collapsed;
            if (FieldRemovedEvent != null)
                FieldRemovedEvent(this, item.Field);
            UpdateFields();
        }
        void      addRow(string field, bool not, object data, bool enabled)
        {
            var value = makeRowValue(field, data, enabled);
            var item = new MetadataItem(field, not, value);
            item.DeletedEvent += new EventHandler(item_DeletedEvent);
            item.SubfieldAddedEvent += new EventHandler(item_SubfieldAddedEvent);
            Grid.SetRow(item, LayoutRoot.Children.Count);
            LayoutRoot.Children.Add(item);
            item.FilterUI = ShowFilteringUI;

            if (ShowFilteringUI)
            {
                TextBlock tb = new TextBlock();
                tb.Text = "and";
                tb.FontSize = 8;
                tb.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
                LayoutRoot.Children.Add(tb);
                tb.IsHitTestVisible = false;
                item.Tag = tb;
            }
            double maxWidth = item.DesiredFieldWidth;
            foreach (FrameworkElement g in LayoutRoot.Children)
                if (g is MetadataItem) {
                    maxWidth = Math.Max(maxWidth, (g as MetadataItem).FieldWidth);
                    (g as MetadataItem).FieldWidth = maxWidth;
                }
            _values.add(field, value is TextBox ? (object)((value as TextBox).Text) : value);
        }

        void      MetadataViewer_DataEditedEvent(MetadataViewer view, string field, object value)
        {

            if (DataEditedEvent != null)
                DataEditedEvent(this, field, value is MetadataDict ? (value as MetadataDict).Dicts.First().Value : value);
        }
        void      NewFieldTextBox_KeyUp(object sender, KeyEventArgs e) 
        {
            if (e.Key == Key.Tab)
            {
                foreach (var item in FieldsAvailableList.Children)
                    if (item is TextBlock && (item as TextBlock).Visibility == Visibility.Visible)
                    {
                        NewFieldTextBox.Text = (item as TextBlock).Text;
                        NewFieldTextBox.SelectionStart = NewFieldTextBox.Text.Length;
                        break;
                    }
            }
            else
                if (e.Key == Key.Return)
                {
                    addField_Click(null, null);
                    NewFieldTextBox.Text = "";
                    foreach (var item in FieldsAvailableList.Children)
                        if (item is TextBlock)
                        {
                            (item as TextBlock).Visibility = Visibility.Visible;
                            (item as TextBlock).Background = Brushes.Transparent;
                        }
                    ShowFilteringUI = ShowFilteringUI;
                }
                else
                {
                    bool first = true;
                    UpdateFields();
                    ShowFilteringUI = ShowFilteringUI;
                    foreach (var item in FieldsAvailableList.Children)
                        if (item is TextBlock && (item as TextBlock).Visibility == Visibility.Visible)
                        {
                            (item as TextBlock).Visibility = Visibility.Collapsed;
                            var suggestion = (item as TextBlock).Text;
                            if (suggestion.IndexOf(NewFieldTextBox.Text) == 0)
                            {
                                if (first)
                                {
                                    first = false;
                                    (item as TextBlock).Background = Brushes.Yellow;
                                }
                                else (item as TextBlock).Background = Brushes.Transparent;
                                (item as TextBlock).Visibility = Visibility.Visible;
                            }
                        }
                }
            e.Handled = true;

        }
        void      tb_TextChanged(object sender, TextChangedEventArgs e)
        {
            TextBox box = sender as TextBox;
            if (DataEditedEvent != null)
            {
                _values.set(box.Tag as string, box.Text);
                if (!_queuedUpdates.ContainsKey((box.Tag as string)))
                    _queuedUpdates.Add((box.Tag as string), box.Text);
                else _queuedUpdates[(box.Tag as string)] = box.Text;
            }
        }
        void      addField_Click(object sender, RoutedEventArgs e) 
        {
            string newField = NewFieldTextBox.Text;
            if ((!ShowFilteringUI && _values.ContainsField(newField)) || newField == "")
                return;
            NewFieldTextBox.Text = "";
            AddField(newField, false, "");
            UpdateFields();
            if (FieldAddedEvent != null)
                FieldAddedEvent(this, newField);
        }
        TextBlock showField(List<string> values, string field)
        {
            foreach (var f in FieldsAvailableList.Children)
                if (f is TextBlock && (f as TextBlock).Text == field)
                    return (f as TextBlock);
            values.Add(field);
            TextBlock tb = new TextBlock();
            tb.Text = field;
            tb.Padding = new Thickness(8, 0, 0, 0);
            tb.FontSize = 11;
            tb.MouseDown += new MouseButtonEventHandler((ss, ee) => {
                NewFieldTextBox.Text = field;
                addField_Click(ss, ee);
            });
            FieldsAvailableList.Children.Add(tb);
            return tb;
        }

        public delegate void DataEdited(MetadataViewer viewer, string field, object value);
        public delegate void FieldChanged(MetadataViewer view, string field);
        public event    DataEdited   DataEditedEvent;
        public event    FieldChanged FieldAddedEvent;
        public event    FieldChanged FieldRemovedEvent;

        public MetadataViewer():this(new MetadataDict(), Guid.Empty, false, "") { }
        public MetadataViewer(MetadataDict pairs, Guid creatorID, bool showCreator, string URL) {
            InitializeComponent();
            MouseLeftButtonDown += new MouseButtonEventHandler((ss, ee) => ee.Handled = true);
            MouseLeftButtonUp   += new MouseButtonEventHandler((ss, ee) => ee.Handled = true);
            Background = new SolidColorBrush(Color.FromArgb(0x1, 0x1, 0x1, 0x1));
            _creatorID = creatorID;
            _showCreator = showCreator;

            UpdateAll(pairs, URL);
        }
        public IEnumerable<FieldDesc> Fields  
        {
            get
            {
                foreach (FrameworkElement ele in LayoutRoot.Children)
                    if (ele is Grid)
                    {
                        var row = ele as MetadataItem;
                        if (row.Visibility != System.Windows.Visibility.Collapsed)
                            yield return new FieldDesc(row.Field, row.Value is TextBox ? (object)(row.Value as TextBox).Text : row.Value, row.Not);
                    }
            }
        }
        public bool     ShowFilteringUI       
        {
            get { return _showFilteringUI; }
            set
            {
                _showFilteringUI     = value;
                _keywords.Visibility = !value ? Visibility.Collapsed : Visibility.Visible;
                _creator.Visibility  = !value ? Visibility.Collapsed : Visibility.Visible;
                _URL.Visibility      = !value ? Visibility.Collapsed : Visibility.Visible;
                _type.Visibility     = !value ? Visibility.Collapsed : Visibility.Visible;
                _deleted.Visibility  = !value ? Visibility.Collapsed : Visibility.Visible;
                foreach (var item in LayoutRoot.Children)
                    if (item is MetadataItem)
                        (item as MetadataItem).FilterUI = value;
            }
        }
        public bool     ShowAllFields         { get; set; }
        public bool     AllowCreationOfFields { get; set; }

        public void     UpdateAll(MetadataDict pairs, string URL)
        {
            _values = new MetadataDict();
            LayoutRoot.Children.Clear();
            // first add all the user-added metadata
            foreach (var dict in pairs.Dicts)
                foreach (var p in dict.Value)
                    if (!DoqDataKeywordExt.IsKeyword(p.Key) && !DoqDataFolderExt.IsFolder(p.Key) && p.Key.IndexOf("_") != 0)
                        addRow(p.Key, false, p.Value, true);

            // then the URL
            if (URL != "")
                addRow("URL", false, URL, _creatorID == DoqDataManager.CreatorID);

            // then the system metadata
            foreach (var dict in pairs.Dicts)
                foreach (var p in dict.Value)
                    if (!DoqDataKeywordExt.IsKeyword(p.Key) && !DoqDataFolderExt.IsFolder(p.Key) && p.Key.IndexOf("_") == 0)
                        addRow(p.Key, false, p.Value, true);

            // and, last, the creator
            if (_showCreator && _creatorID != Guid.Empty)
                addRow("Creator", false, DoqDataManager.Doq(_creatorID).Name, false);

            UpdateFields();
        }
        public void     UpdateFields()
        {
            FieldsAvailableList.Children.RemoveRange(1, FieldsAvailableList.Children.Count - 1);
            List<string> values = new List<string>();
            _creator  = showField(values, "Creator");
            _URL      = showField(values, "URL");
            _type     = showField(values, "Type");
            _keywords = showField(values, "Keywords");
            _deleted  = showField(values, DoqMetadata.Deleted);

            foreach (DoqData d in DoqDataManager.Doqs)
                foreach (KeyValuePair<string, object> pair in d.Metadata)
                    if ((ShowAllFields || pair.Key[0] != '_') && pair.Value is string && 
                        !DoqDataFolderExt.IsFolder(pair.Key) && !DoqDataKeywordExt.IsKeyword(pair.Key ))
                        if (ShowFilteringUI || !_values.ContainsField(pair.Key))
                            showField(values, pair.Key);
            ShowFilteringUI = ShowFilteringUI;
        }
        public void     Flush()       {
            foreach (var pair in _queuedUpdates)
                if (DataEditedEvent != null)
                    DataEditedEvent(this, pair.Key, pair.Value);
        }
        public void     Close()       { Flush(); }
        public object   GetRowValue(string field)
        {
            foreach (FrameworkElement c in LayoutRoot.Children)
                if (c is MetadataItem)
                {
                    var row = c as MetadataItem;
                    if (row.Field == field)
                        return row.Value;
                }
            return null;
        }
        public void     AddRow(string field) { addRow(field, false, "", true); }
        public void     AddField(string newField, bool not, string value) { addRow(newField, not, value, true); }
    }
}
